package com.ccn.qrcode.utils;

import cn.hutool.core.io.resource.ClassPathResource;
import com.ccn.qrcode.common.Constant;
import com.ccn.qrcode.pojo.QrcodeCell;
import com.google.common.collect.Lists;
import com.google.zxing.common.BitMatrix;
import org.checkerframework.checker.nullness.qual.Nullable;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * <pre>
 * Created by Binary Wang on 2017-01-05.
 * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
 * </pre>
 */
public final class MatrixToImageWriter {

    //文件图
    private static final int BLACK = 0xFF000000;
    private static final int WHITE = 0xFFFFFFFF;

    private MatrixToImageWriter() {
    }

    public static BufferedImage toBufferedImage(BitMatrix matrix) throws IOException {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        int beginWidth = 0; int beginHeight = 0;
        int endWidth = 0;   int endHeight = 0;
        //构建宽高列表
        ArrayList<@Nullable Object> imgList = Lists.newArrayList();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
                //二维码有数据，开始做图形折叠
                if (matrix.get(x, y)) {
                    BufferedImg img = new BufferedImg();
                    img.setWidth(x);
                    img.setHeight(y);
                    imgList.add(img);
                    // 找出第一个点
                    if(beginWidth==0){
                        beginWidth =x;
                        beginHeight = y;
                    }
                    // 找到最后一个点
                    if(endWidth<x){
                        endWidth = x;
                    }
                    if(endHeight<y){
                        endHeight=y;
                    }
                }
            }
        }
        //计算rowsize
        int lastRowSize = getRowSize(beginWidth, beginHeight, endWidth, endHeight);
        // logo起始位置，此目的是为logo居中显示
        int lastWidth = 0;
        int lastHeight = 0;
        for (int i = 0; i < imgList.size(); i++) {
            BufferedImg img = (BufferedImg)imgList.get(i);
            int tmpx = img.getWidth();
            int tmpy = img.getHeight();
            if(tmpx < lastWidth+ lastRowSize && tmpy < lastHeight+lastRowSize){
                continue;
            }
            lastWidth = tmpx;
            lastHeight = tmpy;
            overlapImage(image, tmpx, tmpy);
        }
        return image;
    }

    /**
     * 计算每个方格的步长
     * @param beginWidth
     * @param beginHeight
     * @param endWidth
     * @param endHeight
     * @return
     */
    public static int getRowSize(int beginWidth, int beginHeight, int endWidth, int endHeight) {
        int version = Integer.parseInt(Constant.QR_VERSION);
        int versionSize = (version-1)*4+21;
        return (int) Math.ceil((double) (endWidth - beginWidth) / versionSize);
    }

    /**
     * 计算中间点
     */
    public static int getMiddleSize(int beginWidth, int endWidth){
        return (int) Math.ceil(((double)(endWidth-beginWidth) / 2)+beginWidth);
    }

    /**
     * 将logo添加到二维码中间
     *
     * @param image 生成的二维码图片对象
     */
    public static void overlapImage(BufferedImage image, int x, int y) throws IOException {
        InputStream inputStream = ClassLoader.getSystemResourceAsStream("black.png");
//        Random r =new Random();
//        int data = r.nextInt(10);
//        if(data>5){
//            inputStream = ClassLoader.getSystemResourceAsStream("shang.png");
//        }
        MatrixToLogoImageConfig logoConfig = new MatrixToLogoImageConfig();
        BufferedImage logoImg = ImageIO.read(inputStream);
        Graphics2D g = logoImg.createGraphics();
        // 考虑到logo图片贴到二维码中，建议大小不要超过二维码的1/5;
        int width = image.getWidth() / logoConfig.getLogoPart();
        int height = image.getHeight() / logoConfig.getLogoPart();
        int radius = width / 10;

        // 创建一个支持有透明度的图像缓冲区
        BufferedImage buffer = g.getDeviceConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), Transparency.TRANSLUCENT);
        g.dispose();

        // 绘制阴影
        g = buffer.createGraphics();
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, 0.1f));
        g.setColor(Color.BLACK);
        g.fillRoundRect(x + 10, y + 10, width - 20, height, radius, radius);
        g.dispose();

        // 绘制LOGO到缓冲区
        g = buffer.createGraphics();
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1));
        g.drawImage(logoImg, x, y, width, height, null);

        // 将带阴影的图像绘制到二维码上
        g = image.createGraphics();
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f));
        g.drawImage(buffer, 0, 0, image.getWidth(), image.getHeight(), null);
        g.dispose();
    }

    /**
     * 通过指定类型绘图
     * @param image 图片
     * @param x  位置x
     * @param y  位置y
     * @param type 文件用哪个
     * @throws IOException 异常
     */
    public static void overlapImage(BufferedImage image, int x, int y, int type) throws IOException {
        InputStream inputStream = ClassLoader.getSystemResourceAsStream("black.png");
        if(type == 0){
            inputStream = ClassLoader.getSystemResourceAsStream("white.png");
        }
        if(type == 2){
            inputStream = ClassLoader.getSystemResourceAsStream("sanjiao2.png");
        }
        if(type == 3){
            inputStream = ClassLoader.getSystemResourceAsStream("sanjiao3.png");
        }

        MatrixToLogoImageConfig logoConfig = new MatrixToLogoImageConfig();
        BufferedImage logoImg = ImageIO.read(inputStream);
        Graphics2D g = logoImg.createGraphics();
        // 考虑到logo图片贴到二维码中，建议大小不要超过二维码的1/5;
        int width = image.getWidth() / logoConfig.getLogoPart();
        int height = image.getHeight() / logoConfig.getLogoPart();
        int radius = width / 10;

        // 创建一个支持有透明度的图像缓冲区
        BufferedImage buffer = g.getDeviceConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), Transparency.BITMASK);
        g.dispose();

        // 绘制阴影
        g = buffer.createGraphics();
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, 1f));
        g.setColor(Color.blue);
        g.fillRoundRect(x + 10, y + 10, width - 20, height, radius, radius);
        g.dispose();

        // 绘制LOGO到缓冲区
        g = buffer.createGraphics();
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1));
        g.drawImage(logoImg, x, y, width+1, height+1, null);

        // 将带阴影的图像绘制到二维码上
        g = image.createGraphics();
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f));
        g.drawImage(buffer, 0, 0, image.getWidth(), image.getHeight(), null);
        g.dispose();
    }

    /**
     * 在指定位置绘图
     */
    public static void overlapImage(BufferedImage image, List<QrcodeCell> qrcodeCells) throws IOException {
        for (int i = 0; i < qrcodeCells.size(); i++) {
            QrcodeCell qrcodeCell = qrcodeCells.get(i);
            InputStream inputStream = getInputStream(qrcodeCell.getType());
            int x = qrcodeCell.getWidth();
            int y = qrcodeCell.getHeight();
            MatrixToLogoImageConfig logoConfig = new MatrixToLogoImageConfig();
            BufferedImage logoImg = ImageIO.read(inputStream);
            Graphics2D g = logoImg.createGraphics();
            // 考虑到logo图片贴到二维码中，建议大小不要超过二维码的1/5;
            int width = image.getWidth() / logoConfig.getLogoPart();
            int height = image.getHeight() / logoConfig.getLogoPart();
            int radius = width / 10;

            // 创建一个支持有透明度的图像缓冲区
            BufferedImage buffer = g.getDeviceConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), Transparency.BITMASK);
            g.dispose();

            // 绘制阴影
            g = buffer.createGraphics();
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, 1f));
            g.setColor(Color.blue);
            g.fillRoundRect(x + 10, y + 10, width - 20, height, radius, radius);
            g.dispose();

            // 绘制LOGO到缓冲区
            g = buffer.createGraphics();
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1));
            g.drawImage(logoImg, x, y, width+3, height+3, null);


            // 将带阴影的图像绘制到二维码上
            g = image.createGraphics();
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f));
            g.drawImage(buffer, 0, 0, image.getWidth(), image.getHeight(), null);
            g.dispose();

        }
        System.out.println("--");
    }

    /**
     * 根据文件type 找inputStream
     */
    public static InputStream getInputStream(String fileType){
        InputStream inputStream = new ClassPathResource("static/white.png").getStream();
        switch (fileType){
            case "0" : inputStream = new ClassPathResource("static/white.png").getStream(); break;
            case "2" : inputStream = new ClassPathResource("static/2.png").getStream(); break;
            case "2-1" : inputStream = new ClassPathResource("static/2-1.png").getStream(); break;
            case "2-2" : inputStream = new ClassPathResource("static/2-2.png").getStream(); break;
            case "2-3" : inputStream = new ClassPathResource("static/2-3.png").getStream(); break;
            case "2-4" : inputStream = new ClassPathResource("static/2-4.png").getStream(); break;

            case "3" : inputStream = new ClassPathResource("static/3.png").getStream(); break;
            case "3-1" : inputStream = new ClassPathResource("static/3-1.png").getStream(); break;
            case "3-2" : inputStream = new ClassPathResource("static/3-2.png").getStream(); break;
            case "3-3" : inputStream = new ClassPathResource("static/3-3.png").getStream(); break;

            case "6" : inputStream = new ClassPathResource("static/6.png").getStream(); break;
            case "6-1" : inputStream = new ClassPathResource("static/6-1.png").getStream(); break;
            case "6-2" : inputStream = new ClassPathResource("static/6-2.png").getStream(); break;
            case "6-3" : inputStream = new ClassPathResource("static/6-3.png").getStream(); break;
            case "6-4" : inputStream = new ClassPathResource("static/6-4.png").getStream(); break;

            case "8" : inputStream = new ClassPathResource("static/8.png").getStream(); break;
            case "8-1" : inputStream = new ClassPathResource("static/8-1.png").getStream(); break;
            case "8-2" : inputStream = new ClassPathResource("static/8-2.png").getStream(); break;
            case "8-3" : inputStream = new ClassPathResource("static/8-3.png").getStream(); break;
            case "8-4" : inputStream = new ClassPathResource("static/8-4.png").getStream(); break;
        }
        return inputStream;
    }

    /**
     * 为LOGO剪出圆角
     *
     * @param srcImage LOGO图像
     */
    private static BufferedImage clipRound(BufferedImage srcImage) {
        int width = srcImage.getWidth();
        int height = srcImage.getHeight();
        int radius = width / 10;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setClip(new RoundRectangle2D.Double(0, 0, width, height, radius, radius));
        g.drawImage(srcImage, 0, 0, null);
        g.dispose();
        return image;
    }


    public static void writeToFile(BitMatrix matrix, String format, File file)
            throws IOException {
        BufferedImage image = toBufferedImage(matrix);
        if (!ImageIO.write(image, format, file)) {
            throw new IOException("Could not write an image of format " + format + " to " + file);
        }
    }

    public static void writeToStream(BitMatrix matrix, String format, OutputStream stream)
            throws IOException {
        BufferedImage image = toBufferedImage(matrix);
        if (!ImageIO.write(image, format, stream)) {
            throw new IOException("Could not write an image of format " + format);
        }
    }

}
